Skip to content

Add platform migration support#154

Merged
abnegate merged 13 commits into
mainfrom
feat-platform-db-access
May 14, 2026
Merged

Add platform migration support#154
abnegate merged 13 commits into
mainfrom
feat-platform-db-access

Conversation

@premtsd-code
Copy link
Copy Markdown
Contributor

@premtsd-code premtsd-code commented Mar 3, 2026

Summary

  • Add Platform resource class under Resources/Integrations with support for all platform types
  • Add GROUP_INTEGRATIONS resource group and TYPE_PLATFORM constant to Resource
  • Implement exportPlatforms() in Sources/Appwrite using console key authentication with caching
  • Implement importPlatformResource() in Destinations/Appwrite

Summary by CodeRabbit

  • New Features

    • Added platform integration as a first-class migratable resource: export, report, and import flows now handle platform integrations with console-authenticated export and duplicate-aware import.
  • Tests

    • Updated mocks and unit adapters to include platform integration resources and export coverage.
  • Chores

    • Added integration export handlers/placeholders across source adapters and wired integration import/reporting flow in the destination.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds platform/integration support across the migration system. Introduces Resource::TYPE_PLATFORM and a new Platform resource class. Renames Transfer group GROUP_SETTINGS → GROUP_INTEGRATIONS and registers platform resources in public/root lists. Extends Source with getIntegrationsBatchSize and abstract exportGroupIntegrations; Appwrite Source adds console-key fetching/caching (setConsoleKey, getConsoleHeaders), reporting and export flows for platforms. Appwrite Destination accepts a dbForPlatform/projectInternalId, adds importIntegrationsResource and createPlatform (duplicate checks, timestamp preservation, cache purge). CSV, Firebase, JSON, NHost add stub exportGroupIntegrations methods. Tests/mocks updated.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add platform migration support' directly and clearly describes the main change: adding support for platform migrations across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-platform-db-access
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/Migration/Sources/Firebase.php (1)

816-819: Minor: Inconsistent exception message casing.

The exception message uses 'Not implemented' (lowercase 'i'), while other sources (CSV, JSON, NHost) use 'Not Implemented' (uppercase 'I'). Consider standardizing for consistency.

Proposed fix
 protected function exportGroupIntegrations(int $batchSize, array $resources): void
 {
-    throw new \Exception('Not implemented');
+    throw new \Exception('Not Implemented');
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Sources/Firebase.php` around lines 816 - 819, The exception
message in Firebase::exportGroupIntegrations currently uses 'Not implemented';
update the throw in the protected method exportGroupIntegrations(int $batchSize,
array $resources) to throw the same canonical message 'Not Implemented' used by
other sources so the exception casing is consistent across implementations
(locate the throw new \Exception(...) inside exportGroupIntegrations and change
the string).
src/Migration/Sources/Appwrite.php (2)

2065-2073: Consider implementing pagination using $batchSize parameter.

The $batchSize parameter is unused and exportPlatforms fetches all platforms in a single API call. Other export methods implement pagination for scalability. If the platforms API supports pagination, consider adding it:

-private function exportPlatforms(array $consoleHeaders): void
+private function exportPlatforms(array $consoleHeaders, int $batchSize = 25): void
 {
-    $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
+    $lastPlatform = null;
+
+    while (true) {
+        $queries = [Query::limit($batchSize)];
+        if ($lastPlatform) {
+            $queries[] = Query::cursorAfter($lastPlatform);
+        }
+        
+        $response = $this->call('GET', '/projects/' . $this->project . '/platforms?' . http_build_query(['queries' => $queries]), $consoleHeaders);
+        // ... process platforms ...
+        
+        if (count($response['platforms']) < $batchSize) {
+            break;
+        }
+    }

If the API doesn't support pagination or platform counts are expected to be small, consider documenting this or removing $batchSize from the method signature for clarity.

Also applies to: 2105-2129

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Sources/Appwrite.php` around lines 2065 - 2073,
exportGroupIntegrations currently ignores the $batchSize parameter and calls
exportPlatforms which fetches all platforms at once; update exportPlatforms
(and/or the call site in exportGroupIntegrations) to implement pageable fetching
using $batchSize (e.g., loop with offset/page tokens until no more results) and
yield/return batches compatible with exportWithConsoleHeaders, or if the
Appwrite platforms API has no pagination, remove $batchSize from
exportGroupIntegrations’ signature or add a code comment documenting the lack of
pagination; reference the exportGroupIntegrations method, exportPlatforms
function, and exportWithConsoleHeaders helper when making the change.

2043-2058: Consider using $resourceIds parameter for consistency.

The $resourceIds parameter is unused (as flagged by static analysis). Other report* methods like reportAuth, reportStorage, etc. use this parameter to filter results. For consistency, consider adding filtering support:

 private function reportIntegrations(array $resources, array &$report, array $resourceIds = []): void
 {
     if (\in_array(Resource::TYPE_PLATFORM, $resources)) {
         $consoleHeaders = $this->getConsoleHeaders();

         if ($consoleHeaders === null) {
             return;
         }

         try {
-            $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
+            $queries = [];
+            if (!empty($resourceIds[Resource::TYPE_PLATFORM])) {
+                // Note: Add filtering if the API supports it
+            }
+            $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
             $report[Resource::TYPE_PLATFORM] = $response['total'] ?? 0;

Alternatively, remove the unused parameter if filtering isn't applicable for platforms.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Sources/Appwrite.php` around lines 2043 - 2058, The
reportIntegrations method currently ignores the $resourceIds parameter; either
use it to filter the platforms count or remove the unused parameter. To fix,
update reportIntegrations (method name: reportIntegrations) to apply
$resourceIds when calling the platforms endpoint (e.g. include IDs or a filter
in the GET to '/projects/{project}/platforms' and compute
$report[Resource::TYPE_PLATFORM] from the filtered response) or delete the
$resourceIds parameter from the method signature and all callers if platform
filtering is not supported; ensure the try/catch and assignment to
$report[Resource::TYPE_PLATFORM] remain consistent with other report* methods
(e.g., reportAuth/reportStorage) when implementing the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Migration/Sources/Appwrite.php`:
- Around line 2065-2073: exportGroupIntegrations currently ignores the
$batchSize parameter and calls exportPlatforms which fetches all platforms at
once; update exportPlatforms (and/or the call site in exportGroupIntegrations)
to implement pageable fetching using $batchSize (e.g., loop with offset/page
tokens until no more results) and yield/return batches compatible with
exportWithConsoleHeaders, or if the Appwrite platforms API has no pagination,
remove $batchSize from exportGroupIntegrations’ signature or add a code comment
documenting the lack of pagination; reference the exportGroupIntegrations
method, exportPlatforms function, and exportWithConsoleHeaders helper when
making the change.
- Around line 2043-2058: The reportIntegrations method currently ignores the
$resourceIds parameter; either use it to filter the platforms count or remove
the unused parameter. To fix, update reportIntegrations (method name:
reportIntegrations) to apply $resourceIds when calling the platforms endpoint
(e.g. include IDs or a filter in the GET to '/projects/{project}/platforms' and
compute $report[Resource::TYPE_PLATFORM] from the filtered response) or delete
the $resourceIds parameter from the method signature and all callers if platform
filtering is not supported; ensure the try/catch and assignment to
$report[Resource::TYPE_PLATFORM] remain consistent with other report* methods
(e.g., reportAuth/reportStorage) when implementing the change.

In `@src/Migration/Sources/Firebase.php`:
- Around line 816-819: The exception message in
Firebase::exportGroupIntegrations currently uses 'Not implemented'; update the
throw in the protected method exportGroupIntegrations(int $batchSize, array
$resources) to throw the same canonical message 'Not Implemented' used by other
sources so the exception casing is consistent across implementations (locate the
throw new \Exception(...) inside exportGroupIntegrations and change the string).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 037bf4b and 06ea808.

📒 Files selected for processing (12)
  • src/Migration/Destinations/Appwrite.php
  • src/Migration/Resource.php
  • src/Migration/Resources/Integrations/Platform.php
  • src/Migration/Source.php
  • src/Migration/Sources/Appwrite.php
  • src/Migration/Sources/CSV.php
  • src/Migration/Sources/Firebase.php
  • src/Migration/Sources/JSON.php
  • src/Migration/Sources/NHost.php
  • src/Migration/Transfer.php
  • tests/Migration/Unit/Adapters/MockDestination.php
  • tests/Migration/Unit/Adapters/MockSource.php

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Migration/Sources/Appwrite.php`:
- Around line 2043-2059: Ensure reportIntegrations always initializes
$report[Resource::TYPE_PLATFORM] to 0 before any early returns (e.g., when
getConsoleHeaders() returns null) and honor the optional $resourceIds filter:
after successfully calling $this->call('GET', '/projects/.../platforms',
$consoleHeaders) in reportIntegrations, if $resourceIds is non-empty compute the
count as the number of returned platform IDs that intersect $resourceIds
(otherwise use $response['total'] ?? 0), then assign that value to
$report[Resource::TYPE_PLATFORM]; keep the catch block to set it to 0 on
failure.
- Around line 2065-2074: The exportPlatforms call currently ignores pagination
and batchSize; change exportPlatforms to accept an int $batchSize parameter and
implement the same paginated loop pattern used by other exporters (e.g.,
exportSites): build a Query::limit($batchSize) and use
Query::cursorAfter($cursor) to iterate until no more results, accumulate/emit
each page via exportWithConsoleHeaders in exportGroupIntegrations, and honor
root filtering by checking $this->rootResourceId and $this->rootResourceType
(apply a query filter or skip non-matching platform records) so platforms are
fetched in pages and scoped to the requested root resource.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d5286cc3-043e-4038-981d-5bf3f1e49d69

📥 Commits

Reviewing files that changed from the base of the PR and between 06ea808 and 666f015.

📒 Files selected for processing (1)
  • src/Migration/Sources/Appwrite.php

Comment thread src/Migration/Sources/Appwrite.php
Comment thread src/Migration/Sources/Appwrite.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Migration/Transfer.php (1)

61-95: ⚠️ Potential issue | 🔴 Critical

Resolve unresolved merge-conflict blocks before merge.

Lines 61, 95, 144, 150, and 410–418 still include git conflict markers, which makes this file unparsable and risks dropping either database/messaging groups or the integrations group depending on which side is kept.

🔧 Suggested merge direction
-<<<<<<< HEAD
+    public const GROUP_INTEGRATIONS_RESOURCES = [
+        Resource::TYPE_PLATFORM,
+    ];
+
     public const GROUP_DOCUMENTSDB_RESOURCES = [
         Resource::TYPE_DATABASE_DOCUMENTSDB,
         Resource::TYPE_COLLECTION,
         Resource::TYPE_INDEX,
         Resource::TYPE_DOCUMENT
     ];

     public const GROUP_VECTORSDB_RESOURCES = [
         Resource::TYPE_DATABASE_VECTORSDB,
         Resource::TYPE_COLLECTION,
         Resource::TYPE_ATTRIBUTE,
         Resource::TYPE_INDEX,
         Resource::TYPE_DOCUMENT
     ];

     public const GROUP_DATABASES_RESOURCES = [
         Resource::TYPE_DATABASE,
         Resource::TYPE_DATABASE_DOCUMENTSDB,
         Resource::TYPE_DATABASE_VECTORSDB,
         Resource::TYPE_TABLE,
         Resource::TYPE_INDEX,
         Resource::TYPE_COLUMN,
         Resource::TYPE_ROW,
         Resource::TYPE_DOCUMENT,
         Resource::TYPE_COLLECTION,
         Resource::TYPE_ATTRIBUTE
     ];
-
-    public const GROUP_SETTINGS_RESOURCES = [];
->>>>>>> origin/main
     public const ROOT_RESOURCES = [
         Resource::TYPE_BUCKET,
         Resource::TYPE_DATABASE,
         Resource::TYPE_DATABASE_DOCUMENTSDB,
         Resource::TYPE_DATABASE_VECTORSDB,
         Resource::TYPE_FUNCTION,
         Resource::TYPE_SITE,
         Resource::TYPE_USER,
         Resource::TYPE_TEAM,
+        Resource::TYPE_PLATFORM,
+        Resource::TYPE_PROVIDER,
+        Resource::TYPE_TOPIC,
+        Resource::TYPE_MESSAGE,
     ];
             $resources = match ($service) {
                 self::GROUP_FUNCTIONS => array_merge($resources, self::GROUP_FUNCTIONS_RESOURCES),
                 self::GROUP_SITES => array_merge($resources, self::GROUP_SITES_RESOURCES),
                 self::GROUP_STORAGE => array_merge($resources, self::GROUP_STORAGE_RESOURCES),
                 self::GROUP_GENERAL => array_merge($resources, []),
                 self::GROUP_AUTH => array_merge($resources, self::GROUP_AUTH_RESOURCES),
                 self::GROUP_DATABASES => array_merge($resources, self::GROUP_DATABASES_RESOURCES),
+                self::GROUP_DATABASES_TABLES_DB => array_merge($resources, self::GROUP_TABLESDB_RESOURCES),
+                self::GROUP_DATABASES_DOCUMENTS_DB => array_merge($resources, self::GROUP_DOCUMENTSDB_RESOURCES),
+                self::GROUP_DATABASES_VECTOR_DB => array_merge($resources, self::GROUP_VECTORSDB_RESOURCES),
+                self::GROUP_MESSAGING => array_merge($resources, self::GROUP_MESSAGING_RESOURCES),
                 self::GROUP_INTEGRATIONS => array_merge($resources, self::GROUP_INTEGRATIONS_RESOURCES),
                 default => throw new \Exception('No service group found'),
             };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Transfer.php` around lines 61 - 95, This file still contains
git conflict markers; remove the markers (<<<<<<<, =======, >>>>>>>) and
properly merge the two sides so all intended resource group constants exist:
keep GROUP_INTEGRATIONS_RESOURCES (Resource::TYPE_PLATFORM) and also retain the
new GROUP_DOCUMENTSDB_RESOURCES, GROUP_VECTORSDB_RESOURCES,
GROUP_DATABASES_RESOURCES and GROUP_SETTINGS_RESOURCES definitions, ensuring
there are no duplicate constant names or syntax errors; also search for and
remove conflict markers at the other reported locations (around the blocks that
include lines ~144, ~150 and ~410–418) so the PHP parses cleanly and all groups
are preserved.
🧹 Nitpick comments (1)
src/Migration/Destinations/Appwrite.php (1)

2222-2231: Duplicate detection query may have a race condition.

The findOne check followed by createDocument is not atomic. In high-concurrency scenarios, two imports could pass the existence check simultaneously and both attempt to create the platform. The DuplicateException catch on line 2250 handles this correctly as a fallback, so the logic is safe but could log misleading "already exists" messages for legitimate race conditions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Destinations/Appwrite.php` around lines 2222 - 2231, The
duplicate-detection using $this->dbForPlatform->findOne(...) followed by an
early return causes misleading "Platform already exists" status under race
conditions; remove the early return and allow the subsequent createDocument call
(and its DuplicateException handler) to determine the final outcome, or change
the early-path to only short-circuit when you can guarantee exclusivity;
specifically update the block around $this->dbForPlatform->findOne,
Query::equal(...) and $resource->setStatus(Resource::STATUS_SKIPPED, ...) so it
does not preemptively set SKIPPED on a non-atomic check but instead relies on
the existing DuplicateException handling (the catch near the createDocument
call) to set the SKIPPED status for real duplicates caused by race conditions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Migration/Destinations/Appwrite.php`:
- Around line 2220-2258: The createPlatform method is missing its closing brace,
causing validateFieldsForIndexes to be parsed inside it; add a closing "}"
immediately after the final "return true;" in the createPlatform method (the
method that begins with "protected function createPlatform(Platform $resource):
bool") so that the subsequent "private function validateFieldsForIndexes(Index
$resource, UtopiaDocument $table, array &$lengths)" is defined as a separate
method; ensure braces/indentation for createPlatform are balanced.
- Line 2240: The project document access uses a non-existent method
getInternalId(); replace that call with getSequence() on the Document returned
by $this->dbForPlatform->getDocument('projects', $this->project) so the
assignment for 'projectInternalId' uses ->getSequence() instead of
->getInternalId(); update the expression where 'projectInternalId' is set (in
the Destinations\Appwrite migration code) to call getSequence().

In `@src/Migration/Resource.php`:
- Around line 114-121: Remove the unresolved merge conflict markers in the
ALL_RESOURCES array inside the Resource class and ensure the array contains all
resource type constants: retain self::TYPE_PLATFORM and add self::TYPE_PROVIDER,
self::TYPE_TOPIC, self::TYPE_SUBSCRIBER, and self::TYPE_MESSAGE; specifically
edit the ALL_RESOURCES definition to eliminate the <<<<<<<, =======, and >>>>>>>
lines and produce a clean array listing the full set of constants.
- Around line 74-81: The file contains unresolved git conflict markers; remove
the conflict markers (<<<<<<<, =======, >>>>>>>) and restore a single coherent
constants block that includes all required integration types by adding public
const TYPE_PLATFORM = 'platform'; public const TYPE_SUBSCRIBER = 'subscriber';
public const TYPE_MESSAGE = 'message'; (keep existing surrounding formatting and
visibility) so the class compiles cleanly and no markers remain.

In `@tests/Migration/Unit/Adapters/MockDestination.php`:
- Around line 54-61: Resolve the git merge conflict markers in
MockDestination::getSupportedResources(): remove the <<<<<<<, =======, and
>>>>>>> lines and return an array that includes both Resource::TYPE_PLATFORM and
the messaging types Resource::TYPE_PROVIDER, Resource::TYPE_TOPIC,
Resource::TYPE_SUBSCRIBER, and Resource::TYPE_MESSAGE so all expected resource
types are present.

In `@tests/Migration/Unit/Adapters/MockSource.php`:
- Around line 83-90: Resolve the unresolved git conflict markers inside the
MockSource class's getSupportedResources() method by removing the <<<<<<<,
=======, >>>>>>> lines and returning a single array that includes
Resource::TYPE_PLATFORM plus Resource::TYPE_PROVIDER, Resource::TYPE_TOPIC,
Resource::TYPE_SUBSCRIBER, and Resource::TYPE_MESSAGE so all supported resource
types are present.

---

Outside diff comments:
In `@src/Migration/Transfer.php`:
- Around line 61-95: This file still contains git conflict markers; remove the
markers (<<<<<<<, =======, >>>>>>>) and properly merge the two sides so all
intended resource group constants exist: keep GROUP_INTEGRATIONS_RESOURCES
(Resource::TYPE_PLATFORM) and also retain the new GROUP_DOCUMENTSDB_RESOURCES,
GROUP_VECTORSDB_RESOURCES, GROUP_DATABASES_RESOURCES and
GROUP_SETTINGS_RESOURCES definitions, ensuring there are no duplicate constant
names or syntax errors; also search for and remove conflict markers at the other
reported locations (around the blocks that include lines ~144, ~150 and
~410–418) so the PHP parses cleanly and all groups are preserved.

---

Nitpick comments:
In `@src/Migration/Destinations/Appwrite.php`:
- Around line 2222-2231: The duplicate-detection using
$this->dbForPlatform->findOne(...) followed by an early return causes misleading
"Platform already exists" status under race conditions; remove the early return
and allow the subsequent createDocument call (and its DuplicateException
handler) to determine the final outcome, or change the early-path to only
short-circuit when you can guarantee exclusivity; specifically update the block
around $this->dbForPlatform->findOne, Query::equal(...) and
$resource->setStatus(Resource::STATUS_SKIPPED, ...) so it does not preemptively
set SKIPPED on a non-atomic check but instead relies on the existing
DuplicateException handling (the catch near the createDocument call) to set the
SKIPPED status for real duplicates caused by race conditions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0586d6ac-2bcc-411a-9dd6-aa0630e9e8d0

📥 Commits

Reviewing files that changed from the base of the PR and between 666f015 and 4939c67.

📒 Files selected for processing (11)
  • src/Migration/Destinations/Appwrite.php
  • src/Migration/Resource.php
  • src/Migration/Source.php
  • src/Migration/Sources/Appwrite.php
  • src/Migration/Sources/CSV.php
  • src/Migration/Sources/Firebase.php
  • src/Migration/Sources/JSON.php
  • src/Migration/Sources/NHost.php
  • src/Migration/Transfer.php
  • tests/Migration/Unit/Adapters/MockDestination.php
  • tests/Migration/Unit/Adapters/MockSource.php

Comment thread src/Migration/Destinations/Appwrite.php
Comment thread src/Migration/Destinations/Appwrite.php Outdated
Comment thread src/Migration/Resource.php Outdated
Comment thread src/Migration/Resource.php Outdated
Comment thread tests/Migration/Unit/Adapters/MockDestination.php Outdated
Comment thread tests/Migration/Unit/Adapters/MockSource.php Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/Migration/Sources/Appwrite.php (2)

2235-2249: ⚠️ Potential issue | 🟠 Major

Initialize platform report and honor resourceIds filtering.

On Line 2240, returning early leaves \$report[Resource::TYPE_PLATFORM] unset. Also \$resourceIds is currently unused, so filtered reports can be incorrect.

💡 Suggested logic fix
 private function reportIntegrations(array $resources, array &$report, array $resourceIds = []): void
 {
-    if (\in_array(Resource::TYPE_PLATFORM, $resources)) {
-        $consoleHeaders = $this->getConsoleHeaders();
+    if (!\in_array(Resource::TYPE_PLATFORM, $resources, true)) {
+        return;
+    }
+
+    $report[Resource::TYPE_PLATFORM] = 0;
+    $consoleHeaders = $this->getConsoleHeaders();

-        if ($consoleHeaders === null) {
-            return;
-        }
+    if ($consoleHeaders === null) {
+        return;
+    }

-        try {
-            $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
-            $report[Resource::TYPE_PLATFORM] = $response['total'] ?? 0;
-        } catch (\Throwable) {
-            $report[Resource::TYPE_PLATFORM] = 0;
-        }
-    }
+    try {
+        $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
+        $platforms = $response['platforms'] ?? [];
+        if (!empty($resourceIds[Resource::TYPE_PLATFORM])) {
+            $allowed = \array_flip((array) $resourceIds[Resource::TYPE_PLATFORM]);
+            $platforms = \array_filter($platforms, fn (array $p): bool => isset($allowed[$p['$id'] ?? '']));
+            $report[Resource::TYPE_PLATFORM] = \count($platforms);
+            return;
+        }
+        $report[Resource::TYPE_PLATFORM] = $response['total'] ?? \count($platforms);
+    } catch (\Throwable) {
+        // keep fallback 0
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Sources/Appwrite.php` around lines 2235 - 2249, The
reportIntegrations method currently skips setting
$report[Resource::TYPE_PLATFORM] when getConsoleHeaders() returns null and never
respects the $resourceIds filter; update reportIntegrations to always initialize
$report[Resource::TYPE_PLATFORM] (e.g., to 0) before the headers check and, when
$resourceIds is non-empty, only populate the platform count if
Resource::TYPE_PLATFORM is included in $resourceIds (otherwise leave it as 0 or
skip); keep using getConsoleHeaders() and the existing call('GET',
'/projects/'.$this->project.'/platforms', $consoleHeaders) inside the try/catch
to set the count and preserve the catch behavior for exceptions.

2277-2284: ⚠️ Potential issue | 🟠 Major

Use paginated export for platforms and thread batchSize through.

exportGroupIntegrations() accepts \$batchSize, but exportPlatforms() currently performs a single non-paginated request and does not apply root-resource filtering. This can truncate exported platforms.

📌 Directional refactor
 protected function exportGroupIntegrations(int $batchSize, array $resources): void
 {
-    if (\in_array(Resource::TYPE_PLATFORM, $resources)) {
+    if (\in_array(Resource::TYPE_PLATFORM, $resources, true)) {
         $this->exportWithConsoleHeaders(
             Resource::TYPE_PLATFORM,
             Transfer::GROUP_INTEGRATIONS,
-            $this->exportPlatforms(...)
+            fn (array $consoleHeaders) => $this->exportPlatforms($consoleHeaders, $batchSize)
         );
     }
 }

-private function exportPlatforms(array $consoleHeaders): void
+private function exportPlatforms(array $consoleHeaders, int $batchSize): void
 {
-    $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
-    ...
+    // paginate with limit + cursorAfter until page size < $batchSize
+    // and respect rootResourceId/rootResourceType when TYPE_PLATFORM is targeted
 }
For Appwrite REST API v1 / PHP SDK v19, confirm whether `GET /projects/{projectId}/platforms` supports pagination (`limit`, `cursorAfter`) via `queries`, and provide the exact request/query format.

Also applies to: 2343-2367

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Sources/Appwrite.php` around lines 2277 - 2284, The
exportPlatforms call inside exportGroupIntegrations must be converted to a
paginated exporter that respects the passed $batchSize and filters by root
resource: update exportPlatforms to accept (int $batchSize, ?string
$rootResource = null) and implement paginated requests to Appwrite platforms
using the SDK's queries (e.g. include limit=$batchSize and cursorAfter handling)
so each page is yielded/exported; then change the exportGroupIntegrations
invocation to pass $batchSize and the appropriate root resource when calling
$this->exportPlatforms(...) so Transfer::GROUP_INTEGRATIONS export will iterate
all pages rather than a single non-paginated request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Migration/Sources/Appwrite.php`:
- Around line 2235-2250: The file is missing closing braces for three methods
causing a parse error; add the missing closing brace "}" at the end of
reportIntegrations (method reportIntegrations), add the missing closing brace at
the end of exportWithConsoleHeaders (method exportWithConsoleHeaders), and add
the missing closing brace at the end of exportPlatforms (method
exportPlatforms). Also move any docblocks currently injected after those method
bodies (the malformed "@param string $databaseType" and other docblocks) back
above their respective function declarations or remove them so they are not
placed where the closing brace belongs, ensuring each method ends with a proper
'}' before the next docblock or method declaration.

---

Duplicate comments:
In `@src/Migration/Sources/Appwrite.php`:
- Around line 2235-2249: The reportIntegrations method currently skips setting
$report[Resource::TYPE_PLATFORM] when getConsoleHeaders() returns null and never
respects the $resourceIds filter; update reportIntegrations to always initialize
$report[Resource::TYPE_PLATFORM] (e.g., to 0) before the headers check and, when
$resourceIds is non-empty, only populate the platform count if
Resource::TYPE_PLATFORM is included in $resourceIds (otherwise leave it as 0 or
skip); keep using getConsoleHeaders() and the existing call('GET',
'/projects/'.$this->project.'/platforms', $consoleHeaders) inside the try/catch
to set the count and preserve the catch behavior for exceptions.
- Around line 2277-2284: The exportPlatforms call inside exportGroupIntegrations
must be converted to a paginated exporter that respects the passed $batchSize
and filters by root resource: update exportPlatforms to accept (int $batchSize,
?string $rootResource = null) and implement paginated requests to Appwrite
platforms using the SDK's queries (e.g. include limit=$batchSize and cursorAfter
handling) so each page is yielded/exported; then change the
exportGroupIntegrations invocation to pass $batchSize and the appropriate root
resource when calling $this->exportPlatforms(...) so
Transfer::GROUP_INTEGRATIONS export will iterate all pages rather than a single
non-paginated request.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ceee9396-ec71-4744-abb8-16c3f8f4611f

📥 Commits

Reviewing files that changed from the base of the PR and between 4939c67 and 12a4d8e.

📒 Files selected for processing (5)
  • src/Migration/Resource.php
  • src/Migration/Sources/Appwrite.php
  • src/Migration/Transfer.php
  • tests/Migration/Unit/Adapters/MockDestination.php
  • tests/Migration/Unit/Adapters/MockSource.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Migration/Resource.php

Comment thread src/Migration/Sources/Appwrite.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/Migration/Sources/Appwrite.php (2)

2235-2251: ⚠️ Potential issue | 🟠 Major

Initialize platform report count and honor resourceIds in integrations report.

reportIntegrations() can return without setting \$report[Resource::TYPE_PLATFORM], and filtered reports ignore \$resourceIds, which can produce incorrect totals.

💡 Proposed fix
 private function reportIntegrations(array $resources, array &$report, array $resourceIds = []): void
 {
-    if (\in_array(Resource::TYPE_PLATFORM, $resources)) {
-        $consoleHeaders = $this->getConsoleHeaders();
-
-        if ($consoleHeaders === null) {
-            return;
-        }
-
-        try {
-            $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
-            $report[Resource::TYPE_PLATFORM] = $response['total'] ?? 0;
-        } catch (\Throwable) {
-            $report[Resource::TYPE_PLATFORM] = 0;
-        }
+    if (!\in_array(Resource::TYPE_PLATFORM, $resources, true)) {
+        return;
+    }
+
+    $report[Resource::TYPE_PLATFORM] = 0;
+    $consoleHeaders = $this->getConsoleHeaders();
+    if ($consoleHeaders === null) {
+        return;
+    }
+
+    try {
+        $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
+        $platforms = $response['platforms'] ?? [];
+
+        if (!empty($resourceIds[Resource::TYPE_PLATFORM])) {
+            $allowed = \array_flip((array) $resourceIds[Resource::TYPE_PLATFORM]);
+            $platforms = \array_filter(
+                $platforms,
+                fn (array $platform): bool => isset($allowed[$platform['$id'] ?? ''])
+            );
+            $report[Resource::TYPE_PLATFORM] = \count($platforms);
+            return;
+        }
+
+        $report[Resource::TYPE_PLATFORM] = $response['total'] ?? \count($platforms);
+    } catch (\Throwable) {
+        // keep fallback 0
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Sources/Appwrite.php` around lines 2235 - 2251, Initialize
$report[Resource::TYPE_PLATFORM] = 0 at the start of reportIntegrations(), and
when fetching platforms via call('GET', '/projects/' . $this->project .
'/platforms', $consoleHeaders) honor the $resourceIds filter: if $resourceIds is
non-empty, inspect $response['data'] (the returned platform items) and set
$report[Resource::TYPE_PLATFORM] to the count of items whose '$id' (or 'id') is
in $resourceIds; otherwise fall back to $response['total'] ?? 0. Keep the
existing try/catch and ensure the catch leaves $report[Resource::TYPE_PLATFORM]
as 0.

2281-2290: ⚠️ Potential issue | 🟠 Major

Use \$batchSize and paginate platform export (current flow can truncate data).

exportGroupIntegrations() receives \$batchSize, but exportPlatforms() performs a single list call with no cursor loop and no root-platform filter handling. This can export only the first page and ignore targeted root exports.

💡 Proposed fix
 protected function exportGroupIntegrations(int $batchSize, array $resources): void
 {
     if (\in_array(Resource::TYPE_PLATFORM, $resources)) {
         $this->exportWithConsoleHeaders(
             Resource::TYPE_PLATFORM,
             Transfer::GROUP_INTEGRATIONS,
-            $this->exportPlatforms(...)
+            fn (array $consoleHeaders) => $this->exportPlatforms($consoleHeaders, $batchSize)
         );
     }
 }

 /**
  * `@throws` AppwriteException
  */
-private function exportPlatforms(array $consoleHeaders): void
+private function exportPlatforms(array $consoleHeaders, int $batchSize): void
 {
-    $response = $this->call('GET', '/projects/' . $this->project . '/platforms', $consoleHeaders);
-
-    if (empty($response['platforms'])) {
-        return;
-    }
-
-    $platforms = [];
-
-    foreach ($response['platforms'] as $platform) {
-        $platforms[] = new Platform(
-            $platform['$id'] ?? '',
-            $platform['type'] ?? '',
-            $platform['name'] ?? '',
-            $platform['key'] ?? '',
-            $platform['store'] ?? '',
-            $platform['hostname'] ?? '',
-            createdAt: $platform['$createdAt'] ?? '',
-            updatedAt: $platform['$updatedAt'] ?? '',
-        );
-    }
-
-    $this->callback($platforms);
+    $cursor = null;
+    while (true) {
+        $queries = [Query::limit($batchSize)];
+
+        if ($this->rootResourceId !== '' && $this->rootResourceType === Resource::TYPE_PLATFORM) {
+            $queries[] = Query::equal('$id', $this->rootResourceId);
+            $queries[] = Query::limit(1);
+        } elseif ($cursor !== null) {
+            $queries[] = Query::cursorAfter($cursor);
+        }
+
+        $response = $this->call(
+            'GET',
+            '/projects/' . $this->project . '/platforms',
+            $consoleHeaders,
+            ['queries' => $queries]
+        );
+
+        $current = $response['platforms'] ?? [];
+        if (empty($current)) {
+            break;
+        }
+
+        $platforms = [];
+        foreach ($current as $platform) {
+            $platforms[] = new Platform(
+                $platform['$id'] ?? '',
+                $platform['type'] ?? '',
+                $platform['name'] ?? '',
+                $platform['key'] ?? '',
+                $platform['store'] ?? '',
+                $platform['hostname'] ?? '',
+                createdAt: $platform['$createdAt'] ?? '',
+                updatedAt: $platform['$updatedAt'] ?? '',
+            );
+        }
+
+        $this->callback($platforms);
+
+        if ($this->rootResourceId !== '' && $this->rootResourceType === Resource::TYPE_PLATFORM) {
+            break;
+        }
+
+        $cursor = $current[\count($current) - 1]['$id'] ?? null;
+        if (\count($current) < $batchSize) {
+            break;
+        }
+    }
 }
For Appwrite API/SDK v19, what is the exact supported pagination format for GET /v1/projects/{projectId}/platforms (queries[] with limit/cursorAfter), and how should those queries be encoded for REST requests?

Also applies to: 2351-2375

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Migration/Sources/Appwrite.php` around lines 2281 - 2290,
exportGroupIntegrations currently passes $batchSize but exportPlatforms does a
single list call; update exportPlatforms to loop using $batchSize and Appwrite
v19 cursor pagination: call listPlatforms repeatedly with queries[] params
"limit(<batchSize>)" and "cursorAfter(<lastId>)" (encoded as
queries[]=limit(<n>)&queries[]=cursorAfter(<id>) in REST) until no more results,
and honor any root-platform filter when building the queries; reference the
exportGroupIntegrations and exportPlatforms methods and replace the single-call
list with a cursor-based loop that updates cursorAfter from the last item each
iteration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Migration/Destinations/Appwrite.php`:
- Around line 2202-2216: The switch in importIntegrationsResource(Resource
$resource) only handles Resource::TYPE_PLATFORM and then unconditionally sets
status to STATUS_SUCCESS if not STATUS_SKIPPED, allowing unsupported types to be
marked success; add a default branch (or explicit check after the switch) that
fails fast for unknown resource names by setting the resource to a failure
status (or throwing an exception) and/or logging an error, referencing
importIntegrationsResource(), Resource::TYPE_PLATFORM, Resource::STATUS_SKIPPED
and Resource::STATUS_SUCCESS so unsupported integration types are never silently
marked successful.

---

Duplicate comments:
In `@src/Migration/Sources/Appwrite.php`:
- Around line 2235-2251: Initialize $report[Resource::TYPE_PLATFORM] = 0 at the
start of reportIntegrations(), and when fetching platforms via call('GET',
'/projects/' . $this->project . '/platforms', $consoleHeaders) honor the
$resourceIds filter: if $resourceIds is non-empty, inspect $response['data']
(the returned platform items) and set $report[Resource::TYPE_PLATFORM] to the
count of items whose '$id' (or 'id') is in $resourceIds; otherwise fall back to
$response['total'] ?? 0. Keep the existing try/catch and ensure the catch leaves
$report[Resource::TYPE_PLATFORM] as 0.
- Around line 2281-2290: exportGroupIntegrations currently passes $batchSize but
exportPlatforms does a single list call; update exportPlatforms to loop using
$batchSize and Appwrite v19 cursor pagination: call listPlatforms repeatedly
with queries[] params "limit(<batchSize>)" and "cursorAfter(<lastId>)" (encoded
as queries[]=limit(<n>)&queries[]=cursorAfter(<id>) in REST) until no more
results, and honor any root-platform filter when building the queries; reference
the exportGroupIntegrations and exportPlatforms methods and replace the
single-call list with a cursor-based loop that updates cursorAfter from the last
item each iteration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe7b7011-99c4-442c-a44c-5df899e85d2f

📥 Commits

Reviewing files that changed from the base of the PR and between 12a4d8e and 8684335.

📒 Files selected for processing (2)
  • src/Migration/Destinations/Appwrite.php
  • src/Migration/Sources/Appwrite.php

Comment thread src/Migration/Destinations/Appwrite.php
# Conflicts:
#	src/Migration/Destinations/Appwrite.php
#	src/Migration/Source.php
#	src/Migration/Sources/Appwrite.php
#	src/Migration/Sources/CSV.php
#	src/Migration/Sources/Firebase.php
#	src/Migration/Sources/NHost.php
#	tests/Migration/Unit/Adapters/MockSource.php
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 13, 2026

Greptile Summary

This PR introduces platform integration as a migratable resource type, covering the full export → transfer → import pipeline across all source and destination adapters.

  • Adds Platform resource class under Resources/Integrations, the GROUP_INTEGRATIONS group constant, and TYPE_PLATFORM to Resource; wires export dispatch in Source and all concrete sources.
  • Implements paginated exportPlatforms() in Sources/Appwrite using the Project SDK service with console-key auth, and createPlatform() in Destinations/Appwrite with a pre-insert duplicate check against dbForPlatform.

Confidence Score: 5/5

The change is additive and well-contained; known open items (exception swallowing in reportIntegrations, onDuplicate not yet implemented) were already discussed in earlier review rounds and the developer has acknowledged them.

No new unaddressed defects were found in this iteration. The pagination loop, batchSize forwarding, and duplicate-detection logic in createPlatform all work correctly. The two outstanding concerns from prior rounds are tracked and the developer is aware of them.

src/Migration/Sources/Appwrite.php (reportIntegrations silently swallows exceptions) and src/Migration/Transfer.php (orphaned GROUP_SETTINGS_RESOURCES constant) are the only files with lingering known issues from prior review rounds.

Important Files Changed

Filename Overview
src/Migration/Resources/Integrations/Platform.php New Platform resource class with proper constructor, serialization, and group wiring; fromArray uses id key (no $ prefix) which is correct for the internal migration format.
src/Migration/Sources/Appwrite.php Adds platform export with paginated while-loop and Project SDK service; reportIntegrations silently swallows all exceptions (previously flagged); renames $project → $projectId to make room for the new Project service property.
src/Migration/Destinations/Appwrite.php Adds importIntegrationsResource/createPlatform with duplicate-aware import; onDuplicate policy is intentionally not honoured (dev-acknowledged); dbForPlatform and projectInternalId are new required constructor params.
src/Migration/Transfer.php Replaces GROUP_SETTINGS with GROUP_INTEGRATIONS; GROUP_SETTINGS_RESOURCES (empty array) is now orphaned since GROUP_SETTINGS was removed (previously flagged as P2).
src/Migration/Source.php Adds abstract exportGroupIntegrations method and wires the integrations group dispatch; getIntegrationsBatchSize() added for consistency with other group batch-size helpers.
src/Migration/Resource.php Adds TYPE_PLATFORM = 'platform' constant and includes it in the resource-type list; straightforward additive change.

Reviews (7): Last reviewed commit: "Rename source project to projectId; hold..." | Re-trigger Greptile

Comment thread src/Migration/Sources/Appwrite.php Outdated
Comment thread src/Migration/Sources/Appwrite.php Outdated
Comment thread src/Migration/Destinations/Appwrite.php
Comment thread src/Migration/Sources/Appwrite.php
Comment thread src/Migration/Destinations/Appwrite.php
Comment thread src/Migration/Resources/Integrations/Platform.php
Comment thread src/Migration/Sources/Appwrite.php Outdated
$this->functions = new Functions($this->client);
$this->messaging = new Messaging($this->client);
$this->sites = new Sites($this->client);
$this->projectService = new Project($this->client);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's instead change the existing property to projectId and name this project

@abnegate abnegate merged commit 3ee6e12 into main May 14, 2026
4 checks passed
@abnegate abnegate deleted the feat-platform-db-access branch May 14, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants